//----------------------------------------------------------------------------
//
// (C) Copyrights MARIN/MSCN  2005 
//
//----------------------------------------------------------------------------
//
// $Author: A. Abbing $
//
// $Log: $
//
// $Description: $
//
// $End: $
//
//----------------------------------------------------------------------------

#ifndef __mst_Stream_h__
#define __mst_Stream_h__

#include "api.h"
#include "types.h"
#include <string>

#include <iostream>

namespace mst {

/**return the number of elements in a 1D fixed length array*/
template <class T, unsigned int I>
	unsigned int countof( T (&)[I]) { return I; }

/**return the number of elements in a 2D fixed length array*/
template <class T, unsigned int I, unsigned int J>
	unsigned int countof( T (&)[I][J]) { return I*J; }

/**return the number of elements in a 3D fixed length array*/
template <class T, unsigned int I, unsigned int J, unsigned int K>
	unsigned int countof( T (&)[I][J][K]) { return I*J*K; }

/**return the number of elements in a 4D fixed length array*/
template <class T, unsigned int I, unsigned int J, unsigned int K, unsigned int L>
	unsigned int countof( T (&)[I][J][K][L]) { return I*J*K*L; }

/**return the number of elements in a 5D fixed length array*/
template <class T, unsigned int I, unsigned int J, unsigned int K, unsigned int L, unsigned int M>
	unsigned int countof( T (&)[I][J][K][L][M]) { return I*J*K*L*M; }

/*! \def MST_STREAM_OUTPUT() 
\brief Define standard methods of every output stream
*/
#define MST_STREAM_OUTPUT() \
template <class Type> mst::ostream &operator<<(const Type *a) {\
	return serialize_ptr(*this, a);\
}

/*! \def MST_STREAM_INPUT()
\brief Define standard methods of every input stream
*/
#define MST_STREAM_INPUT() \
template <class Type> mst::istream &operator>>(Type *&a) {\
	return deserialize_ptr(*this, a);\
}

/*!
\class stream mst/Stream.h
\ingroup io com
\brief mst::stream serves as base class for all streamers
*/

class MSTAPI stream {
public:
	/*! \class Admin mst/Stream.h
	\brief The administration abstarct base class
	*/
	class Admin {
	public:
		/**constructor*/
		Admin() {}
		/**destructor*/
		virtual ~Admin() {}
		/**data locator method*/
		virtual void *locate(mst::uuid_t id) = 0;
		/** Convert library name, library version and function
		name to a unique code */
		virtual unsigned int libraryCode(const char *libName, 
			const char *version, const char *funcName) = 0;
		/** Convert the unique library id to corresponding
		library name, library version and function name*/
		virtual void libraryCode(unsigned int id, char *libName,
			char *version, char *funcName) = 0;
	};

public:
	/** Construct a mst::Stream object.*/
	explicit stream();

	/** Destruct a mst::Stream object.*/
	virtual ~stream() {}

	/**restart the stream*/
	virtual void restart();

	/**return the endianess of the current hardware*/
	static bool isBigEndian();

	/**set an administration callback object*/
	void setAdmin(Admin *m) { m_admin = m; }

	/**return the current administration callback object*/
	Admin* getAdmin() { return m_admin; }

	/** Convert library name, library version and function
	name to a unique code */
	unsigned int libraryCode(const char *libName, 
		const char *version, const char *funcName) 
	{ return (m_admin) ? m_admin->libraryCode(libName, version, funcName) : 0L; }
	/** Convert the unique library id to corresponding
	library name, library version and function name*/
	void libraryCode(unsigned int id, char *libName,
		char *version, char *funcName)
	{ if (m_admin) m_admin->libraryCode(id, libName, version, funcName); }

	/**locate data with a particular unique id*/
	void *locate(mst::uuid_t id) 
		{ return (m_admin) ? m_admin->locate(id) : 0L; }

	/**enable forced read/writes*/
	void force(bool val=true) { m_force = val; }

	/**return if read/write must be forced*/
	bool forced() const { return m_force; }

        /**absolute seek in the stream*/
        virtual void aseek(size_t pos);

        /**relative seek in the stream*/
        virtual void rseek(size_t pos);

	/** Sets the width of the stream to the specified value */
	virtual signed long width(signed long wide);

	/** Gets the width of the stream */
	virtual signed long width() const;
private:
	stream(const stream &): m_admin(0L), m_force(false) {}
	stream& operator=(const stream &) { return *this; }

private:
	Admin *m_admin;
	bool m_force;
};

/*!
\class ostream mst/Stream.h
\ingroup io com
\brief mst::ostream serves as base class for all output streamers
*/
class MSTAPI ostream: public mst::stream {
public:
	/**constructor*/
	explicit ostream();

	/**destructor*/
	virtual ~ostream() {}

	/**std::string output operator*/
	virtual mst::ostream &operator<<(const std::string &a);

	/**C string output operator*/
	virtual mst::ostream &operator<<(const char *a);

	/**bool output operator*/
	virtual mst::ostream &operator<<(bool a);

	/**char output operator*/
	virtual mst::ostream &operator<<(char a);

	/**short output operator*/
	virtual mst::ostream &operator<<(short a);

	/**int output operator*/
	virtual mst::ostream &operator<<(int a);

	/**long output operator*/
	virtual mst::ostream &operator<<(long a);

	/**unsigned char output operator*/
	virtual mst::ostream &operator<<(unsigned char a);

	/**unsigned short output operator*/
	virtual mst::ostream &operator<<(unsigned short a);

	/**unsigned int output operator*/
	virtual mst::ostream &operator<<(unsigned int a);

	/**unsigned long output operator*/
	virtual mst::ostream &operator<<(unsigned long a);

	/**float output operator*/
	virtual mst::ostream &operator<<(float a);

	/**double output operator*/
	virtual mst::ostream &operator<<(double a);


	/**return the used number of bytes*/
	virtual size_t length() const;

	/**restart the stream*/
	virtual void restart();

   /**absolute seek in the stream*/
   virtual void aseek(size_t pos);

   /**relative seek in the stream*/
   virtual void rseek(size_t pos);

	/** Sets the width of the stream to the specified value */
	virtual signed long width(signed long wide);

	/** Gets the width of the stream */
	virtual signed long width() const;
	
	/**
	 * Serializer templates
	*/
	MST_STREAM_OUTPUT()

};

/*!
\class istream mst/Stream.h
\ingroup io com
\brief mst::istream serves as base class for all output streamers
*/
class MSTAPI istream: public mst::stream {
public:
	/**contructor*/
	explicit istream();

	/**destructor*/
	virtual ~istream() {}

	/**std::string input operator*/
	virtual mst::istream &operator>>(std::string &a);

	/**char* input operator*/
	virtual mst::istream &operator>>(char *&a);

	/**bool input operator*/
	virtual mst::istream &operator>>(bool &a);

	/**char input operator*/
	virtual mst::istream &operator>>(char &a);

	/**short input operator*/
	virtual mst::istream &operator>>(short &a);

	/**int input operator*/
	virtual mst::istream &operator>>(int &a);

	/**long input operator*/
	virtual mst::istream &operator>>(long &a);

	/**unsigned char input operator*/
	virtual mst::istream &operator>>(unsigned char &a);

	/**unsigned short input operator*/
	virtual mst::istream &operator>>(unsigned short &a);

	/**unsigned int input operator*/
	virtual mst::istream &operator>>(unsigned int &a);

	/**unsigned long input operator*/
	virtual mst::istream &operator>>(unsigned long &a);

	/**float input operator*/
	virtual mst::istream &operator>>(float &a);

	/**double input operator*/
	virtual mst::istream &operator>>(double &a);

	/**return the read length in bytes*/
	virtual size_t length() const;

	/** restart the stream*/
	virtual void restart();

	/**absolute seek in the stream*/
   virtual void aseek(size_t pos);

	/**relative seek in the stream*/
   virtual void rseek(size_t pos);

	/** Sets the width of the stream to the specified value */
	virtual signed long width(signed long wide);

	/** Gets the width of the stream */
	virtual signed long width() const;
	
	/**
	 * Deserializer templates
	*/
	MST_STREAM_INPUT()
};

/**
 * Deserializer template for pointer variables
*/
template <class Type> mst::istream& deserialize_ptr(mst::istream &s, Type *&a)
{	bool val;

	s >> val;
	if (val) {
		if (a == 0L) a = new Type;
		s >> *a;
	}
	return s;
}

/**
 * Serializer template for pointer variables
*/
template <class Type> mst::ostream& serialize_ptr(mst::ostream &s, const Type *a)
{	bool val = a != 0L; 
	s << val; 
	if (val) s << *a; 
	return s;
} 

/**
 * Deserializer template for variable length arrays
*/
template <class Type> mst::istream &deserialize(mst::istream &s, Type *&v, unsigned int &len)
{
	s >> len;
	if (len > 0) {
		if (v != 0L) v = new Type[len];
		Type *tmp = v;
		for (unsigned int i=0; i<len; i++, tmp++) s >> *tmp;
	}
	return s;
}

/**
 * Serializer template for variable length arrays
*/
template <class Type> 
mst::ostream &serialize(mst::ostream &s, const Type *v, unsigned int len)
{	const Type *tmp = v;
	s << len;
	for (unsigned int i=0; i<len; i++, tmp++) s << *tmp;
	return s;
}

/*
 * Deserializer templates for fixed length arrays
*/

/** Deserialization template for 1D fixed length arrays */
template <class Type, unsigned int I> 
mst::istream &deserialize(mst::istream &s, Type (&v)[I])
{
	for (unsigned int i=0; i<I; i++) s >> v[i];
	return s;
}

/** Deserialization template for 2D fixed length arrays */
template <class Type, unsigned int I, unsigned int J> 
mst::istream &deserialize(mst::istream &s, Type (&v)[I][J])
{
	for (unsigned int i=0; i<I; i++) deserialize(s,v[i]);
	return s;
}

/** Deserialization template for 3D fixed length arrays */
template <class Type, unsigned int I, unsigned int J, unsigned int K> 
mst::istream &deserialize(mst::istream &s, Type (&v)[I][J][K])
{
	for (unsigned int i=0; i<I; i++) deserialize(s,v[i]);
	return s;
}

/** Deserialization template for 4D fixed length arrays */
template <class Type, unsigned int I, unsigned int J, unsigned int K, unsigned int L> 
mst::istream &deserialize(mst::istream &s, Type (&v)[I][J][K][L])
{
	for (unsigned int i=0; i<I; i++) deserialize(s,v[i]);
	return s;
}

/** Deserialization template for 5D fixed length arrays */
template <class Type, unsigned int I, unsigned int J, unsigned int K, unsigned int L, unsigned int M> 
mst::istream &deserialize(mst::istream &s, Type (&v)[I][J][K][L][M])
{
	for (unsigned int i=0; i<I; i++) deserialize(s,v[i]);
	return s;
}

/**
 * Serializer template for fixed length arrays
*/

/** Serialization template for 1D fixed length arrays */
template <class Type, unsigned int I> 
mst::ostream &serialize(mst::ostream &s, const Type (&v)[I])
{	
	for (unsigned int i=0; i<I; i++) s << v[i];
	return s;
}

/** Serialization template for 2D fixed length arrays */
template <class Type, unsigned int I, unsigned int J> 
mst::ostream &serialize(mst::ostream &s, const Type (&v)[I][J])
{	
	for (unsigned int i=0; i<I; i++) serialize(s,v[i]);
	return s;
}

/** Serialization template for 3D fixed length arrays */
template <class Type, unsigned int I, unsigned int J, unsigned int K> 
mst::ostream &serialize(mst::ostream &s, const Type (&v)[I][J][K])
{	
	for (unsigned int i=0; i<I; i++) serialize(s,v[i]);
	return s;
}

/** Serialization template for 4D fixed length arrays */
template <class Type, unsigned int I, unsigned int J, unsigned int K, unsigned int L> 
mst::ostream &serialize(mst::ostream &s, const Type (&v)[I][J][K][L])
{	
	for (unsigned int i=0; i<I; i++) serialize(s,v[i]);
	return s;
}

/** Serialization template for 5D fixed length arrays */
template <class Type, unsigned int I, unsigned int J, unsigned int K, unsigned int L, unsigned int M> 
mst::ostream &serialize(mst::ostream &s, const Type (&v)[I][J][K][L][M])
{	
	for (unsigned int i=0; i<I; i++) serialize(s,v[i]);
	return s;
}

}

#endif
